package net.sunzc.yunlian.interact.device.mini;

import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.Log;
import com.android.ddmlib.ShellCommandUnresponsiveException;
import com.android.ddmlib.TimeoutException;
import javafx.application.Platform;
import javafx.scene.image.Image;
import net.sunzc.yunlian.interact.device.CommandInteract;
import net.sunzc.yunlian.interact.device.command.CommonCommand;
import net.sunzc.yunlian.utils.Utils;
import net.sunzc.yunlian.view.AndroidScreen;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.*;

import java.io.File;
import java.io.IOException;

/**
 * Created by Administrator on 2016/9/18.
 */
public class Frame2Screen implements Projector, MiniManager.MiniListener {
    private static final String TAG = "Frame2Screen";
    private FrameHeader mFrameHeader;
    private CommandInteract mDevice;
    private MiniManager mMiniManager;
    private AndroidScreen mScreen;

    public Frame2Screen(CommandInteract device) {
        mDevice = device;
        mMiniManager = new MiniManager(device);
        mMiniManager.configureSession().setIdleTime(IdleStatus.READER_IDLE, 10 * 1000);//10s后没有读操作，置为空闲
        mMiniManager.addFilter("decode", new ProtocolCodecFilter(new ProtocolCodecFactory_Minicap()));

        File minicapShareDir = new File(System.getProperty("user.dir"), "\\minicap\\shared\\");
        File minicapSo = new File(minicapShareDir, "android-" + device.getSdk() + "\\" + device.getAbi() + "\\minicap.so");
        mMiniManager.install(minicapSo, false);

        File minicapBinDir = new File(System.getProperty("user.dir"), "\\minicap\\bin\\");
        File minicap = new File(minicapBinDir, device.getAbi() + "\\minicap");
        mMiniManager.install(minicap, true);

        if (!isSupport()) {
            Log.i(TAG, device.getSerialNum() + "手机不支持");
            return;
        }

        mMiniManager.portForward(Utils.getFreePort(), "minicap");

        mMiniManager.setOnReceiveListener(this);

        mMiniManager.start(String.format(
                "LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P %s@%s/0 ",
                device.getSize(),
                device.getFrameSize()
        ));
    }

    private boolean isSupport() {
        String result;
        try {
            result = mDevice.shellExecute(CommonCommand.getCommand(String.format(
                    "LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P %s@%s/0 -t",
                    mDevice.getSize(),
                    mDevice.getSize()
            )));
        } catch (TimeoutException | AdbCommandRejectedException | ShellCommandUnresponsiveException | IOException e) {
            e.printStackTrace();
            Log.e(TAG, "查询是否支持minicap失败---" + e.getMessage());
            return false;
        }
        return result.contains("OK");
    }

    @Override
    public void setProjectScreen(AndroidScreen listener) {
        mScreen = listener;
    }

    @Override
    public void takeScreenCapture(String absolutePath) {
        File remoteScreenImage = new File("/data/local/tmp/screencap.png");
        mMiniManager.start(String.format(
                "LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P %s@%s/0 -s >%s",
                mDevice.getSize(),
                mDevice.getSize(),
                remoteScreenImage.getAbsolutePath()
        ));
        mDevice.pullFile(remoteScreenImage.getAbsolutePath(), absolutePath);
        Log.i(TAG, "截屏完成:" + absolutePath);
    }

    @Override
    public void stopScreenProjector() {
        mMiniManager.stop();
    }

    @Override
    public void restartScreenProjector() {
        mMiniManager.restart();
        mFrameHeader = null;
    }

    @Override
    public void onReceive(Object message) {
        if (mScreen != null)
            Platform.runLater(() -> mScreen.projectScreenImage((Image) message));
        else
            Log.e(TAG, "屏幕没有添加");
    }

    private class ProtocolCodecFactory_Minicap implements ProtocolCodecFactory {
        @Override
        public ProtocolEncoder getEncoder(IoSession session) throws Exception {
            return new ProtocolEncoderAdapter() {
                @Override
                public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
                    // 不用编码器，所以为空
                }
            };
        }

        @Override
        public ProtocolDecoder getDecoder(IoSession session) throws Exception {
            return new FrameDecoder();
        }
    }

    private class FrameDecoder extends CumulativeProtocolDecoder {

        /**
         * @param session
         * @param in
         * @param out
         * @return true表示数据还不够，false表示够了
         * @throws Exception
         */
        @Override
        protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
//            Log.i(TAG, session.getRemoteAddress() + "----" + session.getId() + "--in.remaining : " + in.remaining());
            if (mFrameHeader == null)
                return parseHeader(in);
            else
                return parseImage(in, out);
        }

        private boolean parseHeader(IoBuffer in) {
            if (in.remaining() > 0) {
                in.mark();
                byte[] headerBytes = new byte[24];
                in.get(headerBytes, 0, 24);
                mFrameHeader = new FrameHeader(headerBytes);
                Log.i(TAG, "接收到header" + mFrameHeader);
            }
            return false;
        }

        /**
         * 接收size
         * 接收data
         *
         * @param in
         * @param out
         * @return
         */
        private boolean parseImage(IoBuffer in, ProtocolDecoderOutput out) {
            if (in.remaining() > 0) {//有数据时，读取前8字节判断消息长度
                in.mark();//标记当前位置，以便reset
                int sizeLength = 4;
                byte[] frameSizeBytes = new byte[sizeLength];
                //因为我的前数据包的长度是保存在第0-3字节中，
                in.get(frameSizeBytes);
                int frameSize = Utils.byteArrayToInt(frameSizeBytes);
//                Log.i(TAG, "frameSize : " + frameSize);
                if (frameSize > in.remaining()) {//如果消息内容不够，则重置，相当于不读取size
                    in.reset();
                    return false;//父类接收新数据，以拼凑成完整数据
                } else {
                    byte[] frameBytes = new byte[frameSize];
                    in.get(frameBytes, 0, frameSize);
                    //TODO 把字节转换为Image
                    Image image = Utils.createImageFromByte(frameBytes);
                    out.write(image);
                    if (in.remaining() > 0) {//如果读取内容后还粘了包，就让父类再重读  一次，进行下一次解析
                        return true;
                    }
                }
            }
            return false;//处理成功，让父类进行接收下个包
        }


    }
}
